import './style.css'

// 导入Three.js核心库和扩展模块
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' // 摄像机轨道控制器
import Snake from './Snake' // 贪吃蛇游戏角色
import Candy from './Candy' // 糖果道具
import Rock from './Rock'   // 岩石障碍物
import Tree from './Tree'   // 树木装饰
import lights from './Lights' // 光照系统
import { resolution } from './Params' // 场景分辨率参数
import gsap from 'gsap' // 动画库（用于平滑过渡）
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader' // 字体加载器
import fontSrc from 'three/examples/fonts/helvetiker_bold.typeface.json?url' // 3D字体文件
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry' // 3D文字几何体
import Entity from './Entity' // 基础实体类

// 设备检测（移动端适配）
const isMobile = window.innerWidth <= 768
const loader = new FontLoader()
let font
// 字体加载系统
loader.load(fontSrc, function (loadedFont) {
  font = loadedFont

  printScore() // 字体加载完成后初始化计分板
})

// 配色方案集合（支持主题切换）
const palettes = {
  green: { // 绿色主题
    groundColor: 0x56f854,
    fogColor: 0x39c09f,
    rockColor: 0xebebeb,
    treeColor: 0x639541,
    candyColor: 0x1d5846,
    snakeColor: 0x1d5846,
    mouthColor: 0x39c09f,
  },
  orange: { // 橙色主题
    groundColor: 0xd68a4c,
    fogColor: 0xffac38,
    rockColor: 0xacacac,
    treeColor: 0xa2d109,
    candyColor: 0x614bdd,
    snakeColor: 0xff470a,
    mouthColor: 0x614bdd,
  },
  lilac: { // 紫色主题
    groundColor: 0xd199ff,
    fogColor: 0xb04ce6,
    rockColor: 0xebebeb,
    treeColor: 0x53d0c1,
    candyColor: 0x9900ff,
    snakeColor: 0xff2ed2,
    mouthColor: 0x614bdd,
  },
}

// 主题管理系统
let paletteName = localStorage.getItem('paletteName') || 'green'
let selectedPalette = palettes[paletteName]

const params = {
  ...selectedPalette, // 当前生效的配色参数
}

// 应用配色方案（支持运行时切换）
function applyPalette(paletteName) {
  const palette = palettes[paletteName]
  localStorage.setItem('paletteName', paletteName)

  selectedPalette = palette

  if (!palette) return
  // 更新场景元素颜色
  const {
    groundColor,
    fogColor,
    rockColor,
    treeColor,
    candyColor,
    snakeColor,
    mouthColor,
  } = palette

  planeMaterial.color.set(groundColor) // 地面
  scene.fog.color.set(fogColor) // 雾效
  scene.background.set(fogColor) // 背景

  // 更新实体颜色（岩石、树木等）
  entities
    .find((entity) => entity instanceof Rock)
    ?.mesh.material.color.set(rockColor)
  entities
    .find((entity) => entity instanceof Tree)
    ?.mesh.material.color.set(treeColor)
  // 更新游戏元素
  candies[0].mesh.material.color.set(candyColor)
  snake.body.head.data.mesh.material.color.set(snakeColor)

  snake.body.head.data.mesh.material.color.set(snakeColor)
  snake.mouthColor = mouthColor
  snake.mouth.material.color.set(mouthColor)
  // 更新UI按钮
  btnPlayImg.src = `./btn-play-bg-${paletteName}.png`
}
// 游戏核心参数
let score = 0 // 得分统计

// 网格辅助线（可视化场景坐标系）
const gridHelper = new THREE.GridHelper(
  resolution.x,     // 横向分割数
  resolution.y,     // 纵向分割数
  0xffffff,         // 主网格颜色
  0xffffff          // 次级网格颜色
)
gridHelper.position.set(resolution.x / 2 - 0.5, -0.49, resolution.y / 2 - 0.5) // 居中定位
gridHelper.material.transparent = true
gridHelper.material.opacity = isMobile ? 0.75 : 0.3 // 移动端降低透明度

// 场景初始化
const scene = new THREE.Scene()
scene.background = new THREE.Color(params.fogColor) // 背景

scene.fog = new THREE.Fog(params.fogColor, 5, 40) // 添加雾效

scene.add(gridHelper)  // 添加辅助网格

// 视窗尺寸管理
const sizes = {
  width: window.innerWidth,
  height: window.innerHeight,
}

// 摄像机系统
const fov = 60 // 正交相机（类似人眼）
const camera = new THREE.PerspectiveCamera(fov, sizes.width / sizes.height, 0.1)

// 摄像机位置（移动端与PC不同）
const finalPosition = isMobile
  ? new THREE.Vector3(resolution.x / 2 - 0.5, resolution.x + 15, resolution.y)
  : new THREE.Vector3(
      -8 + resolution.x / 2,
      resolution.x / 2 + 4,
      resolution.y + 6
    )
const initialPosition = new THREE.Vector3(
  resolution.x / 2 + 5,
  4,
  resolution.y / 2 + 4
)
camera.position.copy(initialPosition)

// 渲染器配置
const renderer = new THREE.WebGLRenderer({
  antialias: window.devicePixelRatio < 2, // 抗锯齿（高清屏关闭）
  logarithmicDepthBuffer: true, // 解决远距离渲染问题
})
document.body.appendChild(renderer.domElement)
handleResize() // 初始自适应

// 高级渲染特性
renderer.toneMapping = THREE.ACESFilmicToneMapping // 电影级色调映射
renderer.toneMappingExposure = 1.2 // 曝光强度
renderer.shadowMap.enabled = true // 启用阴影
renderer.shadowMap.type = THREE.VSMShadowMap // 柔和阴影算法

// 摄像机控制器（限制移动方式）
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true    // 惯性滑动
controls.enableZoom = false      // 禁用缩放
controls.enablePan = false       // 禁用平移
controls.enableRotate = false    // 禁用旋转
controls.target.set(resolution.x/2 -2, 0, resolution.y/2 + (isMobile ? 0 : 2))

// 地面
const planeGeometry = new THREE.PlaneGeometry(resolution.x*50, resolution.y*50)
planeGeometry.rotateX(-Math.PI * 0.5)  // 创建水平面为地面
const planeMaterial = new THREE.MeshStandardMaterial({
  color: params.groundColor
})
const plane = new THREE.Mesh(planeGeometry, planeMaterial)
plane.position.set(resolution.x/2-0.5, -0.5, resolution.y/2-0.5) // 对齐网格
plane.receiveShadow = true  // 接收阴影
scene.add(plane)


// 游戏角色初始化
const snake = new Snake({
  scene,
  resolution,
  color: selectedPalette.snakeColor,
  mouthColor: selectedPalette.mouthColor,
})

// 游戏事件系统
snake.addEventListener('updated', function () {

  // 碰撞检测，碰撞上障碍就死亡，重置游戏
  if (snake.checkSelfCollision() || snake.checkEntitiesCollision(entities)) {
    snake.die()
    resetGame()
  }
  // 糖果收集检测
  const headIndex = snake.indexes.at(-1)
  const candyIndex = candies.findIndex(
    (candy) => candy.getIndexByCoord() === headIndex
  )
  // 吃到糖果
  if (candyIndex >= 0) {
    const candy = candies[candyIndex]
    scene.remove(candy.mesh) // 从场景移除被吃掉的糖果
    candies.splice(candyIndex, 1)
    snake.body.head.data.candy = candy
    addCandy() // 生成新的糖果
    score += candy.points // 加分
    printScore() // 更新分数板
  }
})

let scoreEntity // 当前分数板

function printScore() {
  // 等待字体加载完成
  if (!font) {
    return
  }

  if (!score) {
    score = 0
  }
  // 清理旧分数对象（避免内存泄漏）
  if (scoreEntity) {
    scene.remove(scoreEntity.mesh)
    scoreEntity.mesh.geometry.dispose() // 释放几何体内存
    scoreEntity.mesh.material.dispose() // 释放材质内存
  }

  // 创建带倒角的3D文字几何体
  const geometry = new TextGeometry(`${score}`, {
    font: font,
    size: 1,
    depth: 0.1,
    curveSegments: 12,
    bevelEnabled: true,   // 启用倒角
    bevelThickness: 0.1,  // 倒角深度
    bevelSize: 0.1,       // 倒角宽度
    bevelOffset: 0,
    bevelSegments: 5,
  })

  geometry.center() // 几何体居中
  if (isMobile) {
    geometry.rotateX(-Math.PI * 0.5) // 移动端纵向旋转90度
  }
  // 复用蛇头材质创建网格
  const mesh = new THREE.Mesh(geometry, snake.body.head.data.mesh.material)

  // 定位在场景上方
  mesh.position.x = resolution.x / 2 - 0.5
  mesh.position.z = -4
  mesh.position.y = 1.8

  // 启用阴影投射
  mesh.castShadow = true

  // 创建分数实体并加入场景
  scoreEntity = new Entity(mesh, resolution, { size: 0.8, number: 0.3 })
  // 播放入场动画
  scoreEntity.in()
  scene.add(scoreEntity.mesh)
}

// 移动端触摸控制系统 虚拟方向键DOM
const mobileArrows = document.getElementById('mobile-arrows')

function registerEventListener() {
  if (isMobile) {
    // 触摸逻辑核心参数
    const prevTouch = new THREE.Vector2()  // 记录上次触摸位置
    let middle = 1.55     // 屏幕中心参考线
    let scale = 1         // 方向判断灵敏度系数

    // 触摸事件监听
    window.addEventListener('touchstart', (event) => {
      const touch = event.targetTouches[0]
      middle = THREE.MathUtils.clamp(middle, 1.45, 1.65)
      // 将屏幕坐标转换为归一化坐标（-1到1范围）
      let x = (2 * touch.clientX) / window.innerWidth - 1
      let y = (2 * touch.clientY) / window.innerHeight - middle

      // 游戏启动检测
      if (!isRunning) startGame()

      // 方向判断算法（根据坐标象限）
      if (x * scale > y) {
        if (x * scale < -y) {
          snake.setDirection('ArrowUp')
          scale = 3
        } else {
          snake.setDirection('ArrowRight')
          middle += y
          scale = 0.33
        }
      } else {
        if (-x * scale > y) {
          snake.setDirection('ArrowLeft')
          middle += y
          scale = 0.33
        } else {
          snake.setDirection('ArrowDown')
          scale = 3
        }
      }

      prevTouch.x = x
      prevTouch.y = y // 记录本次触摸位置
    })
  } else {
    // 桌面端键盘事件监听
    window.addEventListener('keydown', function (e) {
      const keyCode = e.code

      snake.setDirection(keyCode) // 方向键控制

      if (keyCode === 'Space') { // 空格键暂停/继续
        !isRunning ? startGame() : stopGame()
      } else if (!isRunning) {
        startGame()
      }
    })
  }
}

let isRunning // 游戏运行状态标记

function startGame() {
  if (!snake.isMoving) {
    // 设置240ms间隔的蛇移动循环（约4帧/秒）
    isRunning = setInterval(() => {
      snake.update()
    }, 240)
  }
}

// 暂停游戏
function stopGame() {
  clearInterval(isRunning) // 清除计时器
  isRunning = null // 重置状态
}

// 重置游戏
function resetGame() {
  stopGame() // 停止游戏循环
  score = 0 // 重置积分

  // 清理所有糖果对象
  let candy = candies.pop()
  while (candy) {
    scene.remove(candy.mesh)
    candy = candies.pop()
  }

  // 清理所有实体对象（岩石/树木）
  let entity = entities.pop()
  while (entity) {
    scene.remove(entity.mesh)
    entity = entities.pop()
  }

  // 重新初始化游戏元素
  addCandy()
  generateEntities()
}

const candies = [] // 糖果对象池
const entities = [] // 实体对象池（障碍物）

// 糖果生成逻辑
function addCandy() {
  const candy = new Candy(resolution, selectedPalette.candyColor)

  let index = getFreeIndex() // 获取可用位置索引
  // 设置三维坐标（基于索引的网格布局）
  candy.mesh.position.x = index % resolution.x
  candy.mesh.position.z = Math.floor(index / resolution.x)

  candies.push(candy) // 加入对象池

  candy.in() // 播放生成动画

  scene.add(candy.mesh)
}

addCandy()

// 随机位置生成算法
function getFreeIndex() {
  let index
  let candyIndexes = candies.map((candy) => candy.getIndexByCoord())
  let entityIndexes = entities.map((entity) => entity.getIndexByCoord())

  // 生成随机索引（范围：0到场景总网格数）
  do {
    index = Math.floor(Math.random() * resolution.x * resolution.y)
  } while (
    snake.indexes.includes(index) || // 不与蛇身重叠
    candyIndexes.includes(index) || // 不与现有糖果重叠
    entityIndexes.includes(index) // 不与障碍物重叠
  )

  return index
}

// 障碍物生成逻辑
function addEntity() {
  // 随机生成岩石或树木（50%概率）
  const entity =
    Math.random() > 0.5
      ? new Rock(resolution, selectedPalette.rockColor)
      : new Tree(resolution, selectedPalette.treeColor)

  let index = getFreeIndex() // 获取安全位置

  // 设置坐标（与糖果生成逻辑相同）
  entity.mesh.position.x = index % resolution.x
  entity.mesh.position.z = Math.floor(index / resolution.x)

  entities.push(entity) // 加入对象池

  scene.add(entity.mesh) // 加入场景
}

// 初始实体生成器
function generateEntities() {
  // 生成20个障碍物
  for (let i = 0; i < 20; i++) {
    addEntity()
  }

  // 按距离场景中心排序（优化渲染顺序）
  entities.sort((a, b) => {
    const c = new THREE.Vector3(
      resolution.x / 2 - 0.5,
      0,
      resolution.y / 2 - 0.5
    )

    const distanceA = a.position.clone().sub(c).length()
    const distanceB = b.position.clone().sub(c).length()

    return distanceA - distanceB
  })

  // 使用GSAP实现弹性入场动画
  gsap.from(
    entities.map((entity) => entity.mesh.scale),
    {
      x: 0,
      y: 0,
      z: 0,
      duration: 1,
      ease: 'elastic.out(1.5, 0.5)',
      stagger: {
        grid: [20, 20],
        amount: 0.7,
      },
    }
  )
}

generateEntities()

// 添加光照
scene.add(...lights)

// 生成装饰性树,地图外的
const treeData = [
  new THREE.Vector4(-5, 0, 10, 1),
  new THREE.Vector4(-6, 0, 15, 1.2),
  new THREE.Vector4(-5, 0, 16, 0.8),
  new THREE.Vector4(-10, 0, 4, 1.3),
  new THREE.Vector4(-5, 0, -3, 2),
  new THREE.Vector4(-4, 0, -4, 1.5),
  new THREE.Vector4(-2, 0, -15, 1),
  new THREE.Vector4(5, 0, -20, 1.2),
  new THREE.Vector4(24, 0, -12, 1.2),
  new THREE.Vector4(2, 0, -6, 1.2),
  new THREE.Vector4(3, 0, -7, 1.8),
  new THREE.Vector4(1, 0, -9, 1.0),
  new THREE.Vector4(15, 0, -8, 1.8),
  new THREE.Vector4(17, 0, -9, 1.1),
  new THREE.Vector4(18, 0, -7, 1.3),
  new THREE.Vector4(24, 0, -1, 1.3),
  new THREE.Vector4(26, 0, 0, 1.8),
  new THREE.Vector4(32, 0, 0, 1),
  new THREE.Vector4(28, 0, 6, 1.7),
  new THREE.Vector4(24, 0, 15, 1.1),
  new THREE.Vector4(16, 0, 23, 1.1),
  new THREE.Vector4(12, 0, 24, 0.9),
  new THREE.Vector4(-13, 0, -13, 0.7),
  new THREE.Vector4(35, 0, 10, 0.7),
]
const tree = new Tree(resolution)

treeData.forEach(({ x, y, z, w }) => {
  let clone = tree.mesh.clone()
  clone.position.set(x, y, z)
  clone.scale.setScalar(w)
  scene.add(clone)
})

const rock = new Rock(resolution)
const resX = resolution.x
const rexY = resolution.y

// 生成装饰性石头,地图外的
const rockData = [
  [new THREE.Vector3(-7, -0.5, 2), new THREE.Vector4(2, 8, 3, 2.8)],
  [new THREE.Vector3(-3, -0.5, -10), new THREE.Vector4(3, 2, 2.5, 1.5)],
  [new THREE.Vector3(-5, -0.5, 3), new THREE.Vector4(1, 1.5, 2, 0.8)],
  [new THREE.Vector3(resX + 5, -0.5, 3), new THREE.Vector4(4, 1, 3, 1)],
  [new THREE.Vector3(resX + 4, -0.5, 2), new THREE.Vector4(2, 2, 1, 1)],
  [new THREE.Vector3(resX + 8, -0.5, 16), new THREE.Vector4(6, 2, 4, 4)],
  [new THREE.Vector3(resX + 6, -0.5, 13), new THREE.Vector4(3, 2, 2.5, 3.2)],
  [new THREE.Vector3(resX + 5, -0.5, -8), new THREE.Vector4(1, 1, 1, 0)],
  [new THREE.Vector3(resX + 6, -0.5, -7), new THREE.Vector4(2, 4, 1.5, 0.5)],
  [new THREE.Vector3(-5, -0.5, 14), new THREE.Vector4(1, 3, 2, 0)],
  [new THREE.Vector3(-4, -0.5, 15), new THREE.Vector4(0.8, 0.6, 0.7, 0)],
  [
    new THREE.Vector3(resX / 2 + 5, -0.5, 25),
    new THREE.Vector4(2.5, 0.8, 4, 2),
  ],
  [
    new THREE.Vector3(resX / 2 + 9, -0.5, 22),
    new THREE.Vector4(1.2, 2, 1.2, 1),
  ],
  [
    new THREE.Vector3(resX / 2 + 8, -0.5, 21.5),
    new THREE.Vector4(0.8, 1, 0.8, 2),
  ],
]

rockData.forEach(([position, { x, y, z, w }]) => {
  let clone = new Rock(resolution).mesh
  clone.position.copy(position)
  clone.scale.set(x, y, z)
  clone.rotation.y = w
  scene.add(clone)
})

// 音效
const audio = document.getElementById('audio')
const btnVolume = document.getElementById('btn-volume')
const btnPlay = document.getElementById('btn-play')
const btnPlayImg = document.getElementById('btn-play-img')
// 音效按钮效果
gsap.fromTo(
  btnPlay,
  { autoAlpha: 0, scale: 0, yPercent: -50, xPercent: -50 },
  {
    duration: 0.8,
    autoAlpha: 1,
    scale: 1,
    yPercent: -50,
    xPercent: -50,
    delay: 0.3,
    ease: `elastic.out(1.2, 0.7)`,
  }
)

// 开始游戏
btnPlay.addEventListener('click', function () {
  audio.play()

  gsap.to(camera.position, { ...finalPosition, duration: 2 })
  if (isMobile) {
    gsap.to(controls.target, {
      x: resolution.x / 2 - 0.5,
      y: 0,
      z: resolution.y / 2 - 0.5,
    })
  }
  gsap.to(scene.fog, { duration: 2, near: isMobile ? 30 : 20, far: 55 })

  gsap.to(this, {
    duration: 1,
    scale: 0,
    ease: `elastic.in(1.2, 0.7)`,
    onComplete: () => {
      this.style.visibility = 'hidden'
    },
  })

  registerEventListener()
})

const userVolume = localStorage.getItem('volume')
if (userVolume === 'off') {
  muteVolume()
}

// 音量
const initialVolume = audio.volume

btnVolume.addEventListener('click', function () {
  if (audio.volume === 0) {
    unmuteVolume()
  } else {
    muteVolume()
  }
})

// 静音
function muteVolume() {
  localStorage.setItem('volume', 'off')
  gsap.to(audio, { volume: 0, duration: 1 })
  btnVolume.classList.remove('after:hidden')
  btnVolume.querySelector(':first-child').classList.remove('animate-ping')
  btnVolume.classList.add('after:block')
}

// 解除静音
function unmuteVolume() {
  localStorage.setItem('volume', 'on')
  btnVolume.classList.add('after:hidden')
  btnVolume.querySelector(':first-child').classList.add('animate-ping')
  btnVolume.classList.remove('after:block')
  gsap.to(audio, { volume: initialVolume, duration: 1 })
}

// 主题选择
const topBar = document.querySelector('.top-bar')
const paletteSelectors = document.querySelectorAll('[data-color]')
gsap.to(topBar, {
  opacity: 1,
  delay: 0.5,
  onComplete: () => {
    gsap.to(paletteSelectors, {
      duration: 1,
      x: 0,
      autoAlpha: 1,
      ease: `elastic.out(1.2, 0.9)`,
      stagger: {
        amount: 0.2,
      },
    })
  },
})

paletteSelectors.forEach((selector) =>
  selector.addEventListener('click', function () {
    const paletteName = this.dataset.color
    applyPalette(paletteName)
  })
)

// 加载器
const manager = new THREE.LoadingManager()
const textureLoader = new THREE.TextureLoader(manager)

// 按键新手引导
const wasd = textureLoader.load('./wasd.png')
const arrows = textureLoader.load('./arrows.png')

const wasdGeometry = new THREE.PlaneGeometry(3.5, 2)
wasdGeometry.rotateX(-Math.PI * 0.5)

const planeWasd = new THREE.Mesh(
  wasdGeometry,
  new THREE.MeshStandardMaterial({
    transparent: true,
    map: wasd,
    opacity: isMobile ? 0 : 0.5,
  })
)

const planeArrows = new THREE.Mesh(
  wasdGeometry,
  new THREE.MeshStandardMaterial({
    transparent: true,
    map: arrows,
    opacity: isMobile ? 0 : 0.5,
  })
)

planeArrows.position.set(8.7, 0, 21)
planeWasd.position.set(13, 0, 21)

// 添加按键新手引导
scene.add(planeArrows, planeWasd)

// 使用主题
applyPalette(paletteName)

// 游戏主循环
function tic() {
  controls.update()

  renderer.render(scene, camera)

  requestAnimationFrame(tic)
}

requestAnimationFrame(tic)

// 监听屏幕尺寸变化
window.addEventListener('resize', handleResize)

function handleResize() {
  sizes.width = window.innerWidth
  sizes.height = window.innerHeight

  camera.aspect = sizes.width / sizes.height
  camera.updateProjectionMatrix()

  renderer.setSize(sizes.width, sizes.height)

  const pixelRatio = Math.min(window.devicePixelRatio, 2)
  renderer.setPixelRatio(pixelRatio)
}
